1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50 package com.touchgraph.graphlayout;
51
52 import java.util.*;
53
54 import com.touchgraph.graphlayout.graphelements.*;
55
56 /*** LocalityUtils: Utilities for switching locality. Animation effects
57 * require a reference to TGPanel.
58 *
59 * @author Alexander Shapiro
60 * @version 1.21 $Id: LocalityUtils.java,v 1.1.1.1 2004/02/06 08:44:05 keesj Exp $
61 */
62 public class LocalityUtils {
63
64 TGPanel tgPanel;
65 Locality locality;
66
67 public static final int INFINITE_LOCALITY_RADIUS = Integer.MAX_VALUE;
68
69 ShiftLocaleThread shiftLocaleThread;
70 boolean fastFinishShift=false;
71
72 public LocalityUtils(Locality loc, TGPanel tgp) {
73 locality = loc;
74 tgPanel = tgp;
75 }
76
77 public void fastFinishAnimation() {
78 fastFinishShift = true;
79 }
80
81 /*** Mark for deletion nodes not contained within distHash. */
82 private synchronized boolean markDistantNodes(final Collection subgraph) {
83 final boolean[] someNodeWasMarked = new boolean[1];
84 someNodeWasMarked[0] = false;
85 Boolean x;
86 TGForEachNode fen = new TGForEachNode() {
87 public void forEachNode(Node n) {
88 if(!subgraph.contains(n)) {
89 n.markedForRemoval=true;
90 someNodeWasMarked[0] = true;
91 }
92 }
93 };
94
95 locality.forAllNodes(fen);
96 return someNodeWasMarked[0];
97 }
98
99 private synchronized void removeMarkedNodes() {
100 final Vector nodesToRemove = new Vector();
101
102 TGForEachNode fen = new TGForEachNode() {
103 public void forEachNode(Node n) {
104 if(n.markedForRemoval) {
105 nodesToRemove.addElement(n);
106 n.markedForRemoval=false;
107 }
108 }
109 };
110 synchronized(locality) {
111 locality.forAllNodes(fen);
112 locality.removeNodes(nodesToRemove);
113 }
114 }
115
116 /*** Add to locale nodes within radius distance of a focal node. */
117 private synchronized void addNearNodes(Hashtable distHash, int radius) throws TGException {
118 for ( int r=0; r<radius+1; r++ ) {
119 Enumeration localNodes = distHash.keys();
120 while (localNodes.hasMoreElements()) {
121 Node n = (Node)localNodes.nextElement();
122 if(!locality.contains(n) && ((Integer)distHash.get(n)).intValue()<=r) {
123 n.justMadeLocal = true;
124 locality.addNodeWithEdges(n);
125 if (!fastFinishShift) {
126 try { Thread.sleep(50); }
127 catch (InterruptedException ex) {}
128 }
129 }
130 }
131 }
132 }
133
134 private synchronized void unmarkNewAdditions() {
135 TGForEachNode fen = new TGForEachNode() {
136 public void forEachNode(Node n) {
137 n.justMadeLocal=false;
138 }
139 };
140 locality.forAllNodes(fen);
141 }
142
143 /*** The thread that gets instantiated for doing the locality shift animation. */
144 class ShiftLocaleThread extends Thread {
145 Hashtable distHash;
146 Node focusNode;
147 int radius;
148 int maxAddEdgeCount;
149 int maxExpandEdgeCount;
150 boolean unidirectional;
151
152 ShiftLocaleThread(Node n, int r, int maec, int meec, boolean unid) {
153 focusNode = n;
154 radius = r;
155 maxAddEdgeCount = maec;
156 maxExpandEdgeCount = meec;
157 unidirectional = unid;
158 start();
159
160 }
161
162 public void run() {
163 synchronized (LocalityUtils.this) {
164
165 if (!locality.getCompleteEltSet().contains(focusNode)) return;
166 tgPanel.stopDamper();
167 distHash = GESUtils.calculateDistances(
168 locality.getCompleteEltSet(),focusNode,radius,maxAddEdgeCount,maxExpandEdgeCount,unidirectional);
169 try {
170 if (markDistantNodes(distHash.keySet())) {
171 for (int i=0;i<5&&!fastFinishShift;i++) {
172 Thread.sleep(100);
173 }
174 }
175 removeMarkedNodes();
176 for (int i=0;i<1&&!fastFinishShift;i++) {
177 Thread.sleep(100);
178 }
179 addNearNodes(distHash,radius);
180 for (int i=0;i<4&&!fastFinishShift;i++) {
181 Thread.sleep(100);
182 }
183 unmarkNewAdditions();
184 } catch ( TGException tge ) {
185 System.err.println("TGException: " + tge.getMessage());
186 } catch (InterruptedException ex) {}
187 tgPanel.resetDamper();
188 }
189 }
190 }
191
192 public void setLocale(Node n, final int radius, final int maxAddEdgeCount, final int maxExpandEdgeCount,
193 final boolean unidirectional) throws TGException {
194 if (n==null || radius<0) return;
195 if(shiftLocaleThread!=null && shiftLocaleThread.isAlive()) {
196 fastFinishShift=true;
197 while(shiftLocaleThread.isAlive())
198 try { Thread.sleep(100); }
199 catch (InterruptedException ex) {}
200 }
201 if (radius == INFINITE_LOCALITY_RADIUS || n==null) {
202 addAllGraphElts();
203 tgPanel.resetDamper();
204 return;
205 }
206
207 fastFinishShift=false;
208 shiftLocaleThread=new ShiftLocaleThread(n, radius, maxAddEdgeCount, maxExpandEdgeCount, unidirectional);
209 }
210
211 public void setLocale(Node n, final int radius) throws TGException {
212 setLocale(n,radius,1000,1000, false);
213 }
214
215 public synchronized void addAllGraphElts() throws TGException {
216 locality.addAll();
217 }
218
219 /*** Add to locale nodes that are one edge away from a given node.
220 * This method does not utilize "fastFinishShift" so it's likely that
221 * synchronization errors will occur.
222 */
223 public void expandNode(final Node n) {
224 new Thread() {
225 public void run() {
226 synchronized (LocalityUtils.this) {
227 if (!locality.getCompleteEltSet().contains(n)) return;
228 tgPanel.stopDamper();
229 for(int i=0;i<n.edgeCount();i++) {
230 Node newNode = n.edgeAt(i).getOtherEndpt(n);
231 if (!locality.contains(newNode)) {
232 newNode.justMadeLocal = true;
233 try {
234 locality.addNodeWithEdges(newNode);
235 Thread.sleep(50);
236 } catch ( TGException tge ) {
237 System.err.println("TGException: " + tge.getMessage());
238 } catch ( InterruptedException ex ) {}
239 }
240 else if (!locality.contains(n.edgeAt(i))) {
241 locality.addEdge(n.edgeAt(i));
242 }
243 }
244 try { Thread.sleep(200); }
245 catch (InterruptedException ex) {}
246 unmarkNewAdditions();
247 tgPanel.resetDamper();
248 }
249 }
250 }.start();
251 }
252
253 /*** Hides a node, and all the nodes attached to it. */
254 public synchronized void hideNode( final Node hideNode ) {
255 if (hideNode==null) return;
256 new Thread() {
257 public void run() {
258 synchronized(LocalityUtils.this) {
259 if (!locality.getCompleteEltSet().contains(hideNode)) return;
260
261 locality.removeNode(hideNode);
262 if (hideNode==tgPanel.getSelect()) {
263 tgPanel.clearSelect();
264 }
265
266
267 Collection subgraph = GESUtils.getLargestConnectedSubgraph(locality);
268 markDistantNodes(subgraph);
269 tgPanel.repaint();
270 try { Thread.sleep(200); }
271 catch (InterruptedException ex) {}
272 removeMarkedNodes();
273
274 tgPanel.resetDamper();
275 }
276 }
277 }.start();
278 }
279
280 /*** Opposite of expand node, works like hide node except that the selected node is not hidden.*/
281 public synchronized void collapseNode( final Node collapseNode ) {
282 if (collapseNode==null) return;
283 new Thread() {
284 public void run() {
285 synchronized(LocalityUtils.this) {
286 if (!locality.getCompleteEltSet().contains(collapseNode)) return;
287
288 locality.removeNode(collapseNode);
289 Collection subgraph = GESUtils.getLargestConnectedSubgraph(locality);
290 markDistantNodes(subgraph);
291 try {
292 locality.addNodeWithEdges(collapseNode);
293 }
294 catch (TGException tge) { tge.printStackTrace(); }
295 tgPanel.repaint();
296 try { Thread.sleep(200); }
297 catch (InterruptedException ex) {}
298 removeMarkedNodes();
299
300 tgPanel.resetDamper();
301 }
302 }
303 }.start();
304 }
305 }